# -*- coding: utf-8 -*-
# @Time    : 2024/6/24 11:46
# @Author  : yujiahao
# @File    : 06_pandas_reindex_iter_storing.py
# @description: pandas 重置索引、遍历和排序


# todo 1、Pandas reindex 重置索引
'''
重置索引（reindex）
    可以更改原 DataFrame 的行标签或列标签，并使更改后的行、列标签与 DataFrame 中的数据逐一匹配。
    通过重置索引操作，可以完成对现有数据的重新排序。如果重置的索引标签在原 DataFrame 中不存在，那么该标签对应的元素值将全部填充为 NaN。
'''

import pandas as pd
import numpy as np


def pandas_reindex():
    # todo 1.1、 reindex()

    # 重置行列标签
    N = 20

    df = pd.DataFrame({
        'A': pd.date_range(start='2016-01-01', periods=N, freq='D'),
        'x': np.linspace(0, stop=N - 1, num=N),
        'y': np.random.rand(N),
        'C': np.random.choice(['Low', 'Medium', 'High'], N).tolist(),
        'D': np.random.normal(100, 10, size=(N)).tolist()
    })

    print('原始数据', df)
    # 重置行、列索引标签
    df_reindexed = df.reindex(index=[0, 2, 5], columns=['A', 'C', 'B'])
    print('重置行，列索引标签', df_reindexed)

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    # todo 1.2、reindex_like()

    # 现有 a、b 两个 DataFrame 对象，如果想让 a  的行索引与 b 相同，可以使用 reindex_like() 方法。（看源码，写的很清楚）示例如下：
    '''
    reindex_like() 提供了一个可选的参数method，使用它来填充相应的元素值，参数值介绍如下：
        pad/ffill：向前填充值；缺失的值会被填充为前面最近一个非缺失值，所以所有新填充的值都和前一个最近的值全部都一样
        bfill/backfill：向后填充值；
        nearest：从距离最近的索引值开始填充;
        limit，该参数用来控制填充的最大行数。
    '''
    df1 = pd.DataFrame(np.random.randn(6, 3), columns=['col1', 'col2', 'col3'])
    df2 = pd.DataFrame(np.random.randn(2, 3), columns=['col1', 'col2', 'col3'])
    # 使df2和df1行标签相同
    print(df2.reindex_like(df1))
    # 向前填充元素值
    print(df2.reindex_like(df1, method='ffill', limit=2))

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    # todo 1.3、rename()

    df1 = pd.DataFrame(np.random.randn(6, 3), columns=['col1', 'col2', 'col3'])
    print(df1)
    # 对行和列重新命名
    print(df1.rename(columns={'col1': 'c1', 'col2': 'c2'}, index={0: 'apple', 1: 'banana', 2: 'durian'}))

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    # 2、todo Pandas iteration遍历

    '''遍历是众多编程语言中必备的一种操作，比如 Python 语言通过 for 循环来遍历列表结构。那么 Pandas 是如何遍历 Series 和 DataFrame 结构呢？
    我们应该明确，它们的数据结构类型不同的，遍历的方法必然会存在差异。
    对于 Series 而言，我们可以把它当做一维数组进行遍历操作；
    而像 DataFrame 这种二维数据表结构，则类似于遍历 Python 字典。
    通过for遍历后，Series 可直接获取相应的 value，而 DataFrame 则会获取列标签。'''


def pandas_iteration():
    # todo 2.1、fro循环
    N = 20
    df = pd.DataFrame({
        'A': pd.date_range(start='2016-01-01', periods=N, freq='D'),
        'x': np.linspace(0, stop=N - 1, num=N),
        'y': np.random.rand(N),
        'C': np.random.choice(['Low', 'Medium', 'High'], N).tolist(),
        'D': np.random.normal(100, 10, size=(N)).tolist()
    })

    print('打印一下这个DF看看', df)

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    for col in df:
        print('使用for循环DF打印的是列标签：', col)

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    # todo 2.2、内置迭代方法

    '''
    内置迭代方法
    如果想要遍历 DataFrame 的每一行，我们下列函数：
        1) items()：以键值对 (key,value) 的形式遍历；（较低一点的版本是iteritems（）被弃用了）
        2) iterrows()：以 (row_index,row) 的形式遍历行;
        3) itertuples()：使用已命名元组的方式对行遍历。
        
    '''
    df = pd.DataFrame(np.random.randn(4, 3), columns=['col1', 'col2', 'col3'])

    print('看看原始的\n', df)

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    for key, value in df.items():
        print('用for循环打印：', key, value)

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    '''该方法按行遍历，返回一个迭代器，以行索引标签为键，以每一行数据为值。
    注意：iterrows() 遍历行，其中 0,1,2 是行索引而 col1,col2,col3 是列索引。
    '''
    for row_index, row in df.iterrows():
        print(row_index, row)
        # 迭代器返回的是原对象的副本，所以，如果在迭代过程中修改元素值，不会影响原对象，这一点需要大家注意。
        row['a'] = 15
        print('这是遍历行，返回的是副本：\n')

    print('修改迭代后的东西原来的不变\n', df)

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    # 3) itertuples

    '''itertuples() 同样将返回一个迭代器，该方法会把 DataFrame 的每一行生成一个元组'''
    for row in df.itertuples():
        print(row)


# todo 3、Pandas sorting排序

'''Pands 提供了两种排序方法，分别是按标签排序和按数值排序'''


def pandas_sorting():
    # 这里DF的行列都是乱排序的
    unsorted_df = pd.DataFrame(np.random.randn(10, 2), index=[1, 6, 4, 2, 3, 5, 9, 8, 0, 7], columns=['col2', 'col1'])
    print('原始的数据：\n', unsorted_df)

    # todo 3.1、按标签排序
    '''
    使用 sort_index() 方法对行标签排序，指定轴参数（axis）或者排序顺序。或者可以对 DataFrame 进行排序。
    通过将布尔值传递给ascending参数，可以控制排序的顺序（行号顺序），默认为True是按照升序，False是降序
    '''
    # todo 3.2、按列排序
    '''通过给 axis 轴参数传递 0 或 1，可以对列标签进行排序。默认情况下，axis=0 表示按行排序；而 axis=1 则表示按列排序'''

    sorted_df = unsorted_df.sort_index(ascending=False)
    print('按照标签降序排序：\n', sorted_df)

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    # todo 3.3、按值排序
    '''与标签排序类似，sort_values() 表示按值排序。它接受一个by参数，该参数值是要排序数列的  DataFrame 列名。'''
    unsorted_df = pd.DataFrame({'col1': [2, 1, 1, 1], 'col2': [1, 3, 2, 4]})
    sorted_df = unsorted_df.sort_values(by=['col2', 'col1'])
    print('按照指定列的数值进行排序：\n', sorted_df)

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    # todo 3.4、排序算法
    '''
    sort_values() 提供了参数kind用来指定排序算法。这里有三种排序算法：
        - mergesort
        - heapsort
        - quicksort
    默认为 quicksort(快速排序) ，其中 Mergesort 归并排序是最稳定的算法。
    '''

    sorted_df = unsorted_df.sort_values(by='col1', kind='mergesort')
    print('选择归并排序：\n', sorted_df)


def main():
    # pandas_reindex()
    # pandas_iteration()
    pandas_sorting()


if __name__ == '__main__':
    main()
